import 'dotenv/config';
import express from 'express';
import { SignJWT, jwtVerify } from 'jose';
import { TextEncoder } from 'node:util';
import { isValidatedUser } from './validate.mjs';

const JWT_SECRET = process.env.JWT_SECRET;
if (!JWT_SECRET || typeof JWT_SECRET !== 'string' || JWT_SECRET.length < 16) {
  console.error('Invalid JWT_SECRET');
  process.exit(1);
}
const secret = new TextEncoder().encode(JWT_SECRET);
const alg = 'HS256';
const issuers = ['issuer1', 'issuer2'];
const audiences = ['audience1', 'audience2', 'audience3'];

// create a express app
const app = express();

// json as body parser
app.use(express.json());

app.get('/', (req, res) => {
  res.send('Hello World!');
});

// login
app.post('/auth', async (req, res) => {
  try {
    const isUser = await isValidatedUser(req.body);
    if (!isUser) {
      return res.status(401).json({ msg: 'unauthorized' });
    }
    const jwt = await new SignJWT({ username: req.body.username })
      .setProtectedHeader({ alg })
      .setIssuedAt()
      .setIssuer(issuers[0])
      .setAudience(audiences[0])
      .setSubject(String(req.body.username))
      .setExpirationTime('2h')
      .sign(secret);
    res.setHeader('Cache-Control', 'no-store');
    res.json({ data: { jwt }, msg: 'ok' });
  } catch (error) {
    res.status(500).json({ msg: 'internal_error' });
  }
});

// verify
app.get('/verify', async (req, res) => {
  const auth = req.headers.authorization;
  if (!auth || typeof auth !== 'string' || !auth.startsWith('Bearer ')) {
    return res.status(401).json({ msg: 'unauthorized' });
  }
  const token = auth.slice('Bearer '.length);
  if (!token) {
    return res.status(401).json({ msg: 'unauthorized' });
  }
  try {
    const { payload } = await jwtVerify(token, secret, {
      issuer: issuers,
      audience: audiences,
      clockTolerance: 5,
    });
    res.setHeader('Cache-Control', 'no-store');
    res.json({ data: payload, msg: 'ok' });
  } catch (error) {
    res.status(401).json({ msg: 'unauthorized' });
  }
});

app.use((req, res) => {
  res.status(404).json({ msg: 'not_found' });
});

app.use((err, req, res, next) => {
  res.status(500).json({ msg: 'internal_error' });
});

const PORT = Number(process.env.PORT) || 3000;
app.listen(PORT);
